/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package pe.edu.pucp.dp1.procesamiento.controlador;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
//import pe.edu.pucp.dp1.lectura.modelo.ObjCar;
import Shared.ObjCar;
import pe.edu.pucp.dp1.persistencia.bd.BaseDatos;
import pe.edu.pucp.dp1.persistencia.bd.Punto;
import pe.edu.pucp.dp1.procesamiento.algoritmo.Genetico;
import pe.edu.pucp.dp1.procesamiento.modelo.ConfiguracionInterseccion;
import pe.edu.pucp.dp1.procesamiento.modelo.InterseccionEntrada;
import pe.edu.pucp.dp1.procesamiento.modelo.InterseccionOptima;
//import pe.edu.pucp.dp1.procesamiento.modelo.MatrizInterseccionSalida;
import Shared.MatrizInterseccionSalida;
//import pe.edu.pucp.dp1.procesamiento.modelo.InterseccionSalida;
import Shared.InterseccionSalida;
import pe.edu.pucp.dp1.gui.controlador.GestorGUI;
import pe.edu.pucp.dp1.interfaz.sistemadeer.SistemaDEER;
import pe.edu.pucp.dp1.procesamiento.modelo.MatrizInterseccionEntrada;
import pe.edu.pucp.dp1.procesamiento.modelo.MatrizInterseccionOptima;
import pe.edu.pucp.dp1.procesamiento.modelo.TablaConfiguracion;

/**
 *
 * @author Edgard
 */
public class GestorInterseccion {

    public static Propiedades properties = Propiedades.getInstance();

    public static GestorGUI gestorGUI = GestorGUI.getInstance();
    
    static final int LARGO_ZONA = Integer.valueOf(properties.getProperties().getProperty("LARGO_ZONA"));
    static final int NUM_VEH = Integer.valueOf(properties.getProperties().getProperty("NUM_VEH"));

    private int nZonasLargo = Integer.valueOf(properties.getProperties().getProperty("nZonasLargo"));
    private int nZonasAlto = Integer.valueOf(properties.getProperties().getProperty("nZonasAlto"));

    private int periodoSemaforo = Integer.valueOf(properties.getProperties().getProperty("PeriodoSemaforo"));
    private int periodoGenetico = Integer.valueOf(properties.getProperties().getProperty("PeriodoGenetico"));

    HiloZona[][] matrizHiloZona = new HiloZona[nZonasLargo][nZonasAlto];
    
    private boolean[][] matrizZonaPatron = new boolean[nZonasLargo][nZonasAlto];

    private boolean hiloGenEjecucion = false;

    /* Representa toda la configuracion de semaforos actual de todo el mapa
     Primero se inicializa con configuracion 60-60 para todo el mapa,
     Cada 10 min se  determina si a una zona debe de aplicarse o no el AG, segun 
     los patrones, y se actualiza la estructura. De esta manera el hilo que
     actualiza los estados de semaforos podrá seguir ejecutandose
     */
    private MatrizInterseccionOptima[][] matrizTotalOptima = new MatrizInterseccionOptima[nZonasLargo][nZonasAlto];
    private MatrizInterseccionSalida[][] matrizTotalSalida = new MatrizInterseccionSalida[nZonasLargo][nZonasAlto];
    private MatrizInterseccionEntrada[][] matrizTotalEntrada = new MatrizInterseccionEntrada[nZonasLargo][nZonasAlto];//CONSULTA EDUARDO

    private static GestorInterseccion instance;

    public static GestorInterseccion getInstance() {
        if (instance == null) {
            instance = new GestorInterseccion();
        }
        return instance;
    }

    public static TablaConfiguracion tablaConfig = TablaConfiguracion.getInstance();

    /* Edgard: Por el momento solo para una zona. Ejm en coord (0,0) en matriz (0,0)
     En coord (200,300) en matriz (2,3)
     */
    public void IniMatInterSalida(MatrizInterseccionSalida matrizIntSalida, MatrizInterseccionEntrada matrizIntEntrada, MatrizInterseccionOptima matrizIntOptima) {
        //Inicializamos todas las intersecciones en Tiempo = 60s y Estado = 0 VerdeHorizontal
        InterseccionSalida[][] auxMatInterSalida = new InterseccionSalida[LARGO_ZONA][LARGO_ZONA];
        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntEntrada.getMatrizInterseccionEntrada()[i][j] != null) {
                    int auxConfig = matrizIntOptima.getMatrizInterseccionOptima()[i][j].getConfiguracion();
                    int auxDurTiempoVerHor = tablaConfig.ObtenerConfiguracion(auxConfig).getTiempoVerdeHor();
                    InterseccionSalida interSalida = new InterseccionSalida(auxDurTiempoVerHor, 0);// Siempre inicia en verde horizontal
                    auxMatInterSalida[i][j] = interSalida;
                }
            }
        }
        matrizIntSalida.setMatrizInterseccionSalida(auxMatInterSalida);
    }

    public void ActMatInterSalida(MatrizInterseccionSalida matrizIntSalida, MatrizInterseccionOptima matrizIntOptima) {

        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntSalida.getMatrizInterseccionSalida()[i][j] != null) {
                    int auxTiempoRestante = matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    if (auxTiempoRestante == 1) {//Si le resta 1 seg, le cambia de estadoVerdeEjeX

                        //matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(3);
                        int auxEstado = matrizIntSalida.getMatrizInterseccionSalida()[i][j].getEstadoVerdeEjeX();
                        if (auxEstado == 0) {//de 0 a 1, y de 1 a 0

                            int auxConfig = matrizIntOptima.getMatrizInterseccionOptima()[i][j].getConfiguracion();
                            int auxNuevoTiempo = tablaConfig.ObtenerConfiguracion(auxConfig).getTiempoVerdeVer();//Cambio a tiempo Vertical
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setEstadoVerdeEjeX(1);
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(auxNuevoTiempo);
                        } else {
                            int auxConfig = matrizIntOptima.getMatrizInterseccionOptima()[i][j].getConfiguracion();
                            int auxNuevoTiempo = tablaConfig.ObtenerConfiguracion(auxConfig).getTiempoVerdeHor();//Cambio a tiempo Vertical
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setEstadoVerdeEjeX(0);
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(auxNuevoTiempo);
                        }
                    } else {
                        //Sino hay cambio de estado
                        matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(--auxTiempoRestante);
                    }
                }
            }
        }
    }

    /* Edgard: Imprime la zona como esta en la matriz */
    public void ImpMatInterSalida(MatrizInterseccionSalida matrizIntSalida) {

        String cadena = "";
        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntSalida.getMatrizInterseccionSalida()[i][j] != null) {
                    cadena = "[";

                    if (matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante() >= 100) {
                        cadena += matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    } else if (matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante() >= 10) {
                        cadena += "0" + matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    } else {
                        cadena += "00" + matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    }

                    cadena += "|";

                    if (matrizIntSalida.getMatrizInterseccionSalida()[i][j].getEstadoVerdeEjeX() == 0) {
                        cadena += "H";
                    } else {
                        cadena += "V";
                    }

                    cadena += "] ";
                    System.out.print(cadena);
                } else {
                    System.out.print("[     ] ");
                }
            }
            System.out.println("\n");
        }
    }

    /* Edgard: Imprime la zona como se mostraria en el mapa */
    public void ImpMatInterSalidaMapa(MatrizInterseccionSalida matrizIntSalida) {

        String cadena = "";
        for (int i = LARGO_ZONA - 1; i >= 0; i--) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntSalida.getMatrizInterseccionSalida()[i][j] != null) {
                    cadena = "[";

                    if (matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante() >= 100) {
                        cadena += matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    } else if (matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante() >= 10) {
                        cadena += "0" + matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    } else {
                        cadena += "00" + matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    }

                    cadena += "|";

                    if (matrizIntSalida.getMatrizInterseccionSalida()[i][j].getEstadoVerdeEjeX() == 0) {
                        cadena += "H";
                    } else {
                        cadena += "V";
                    }

                    cadena += "] ";
                    System.out.print(cadena);
                } else {
                    System.out.print("[     ] ");
                }
            }
            System.out.println("\n");
        }
    }

    /* Edgard: SOLO PARA PROBAR CONEXION
     Inicializa la matriz de entrada con semaforos cada 400 metros
     */
    public void IniMatInterEntrada(MatrizInterseccionEntrada matrizIntEntrada) {
        /* Inicializaremos la matriz para una zona, como si los semaforos esten cada 400 metros */
        InterseccionEntrada[][] auxMatInterEntrada = new InterseccionEntrada[LARGO_ZONA][LARGO_ZONA];

        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                //if ((i == 4 && j == 4) || (i == 8 && j == 4) || (i == 3 && j == 7) || (i == 7 && j == 7)) {
                if ((i % 4 == 0) && (j % 4 == 0)) {
                    InterseccionEntrada interEntrada = new InterseccionEntrada(0, 0, 0, 0);
                    auxMatInterEntrada[i][j] = interEntrada;
                }
            }
        }
        matrizIntEntrada.setMatrizInterseccionEntrada(auxMatInterEntrada);
    }

    /* Edgard: Funcion para verificar el estado de una interseccion para un vehiculo */
    public int VerEstadoInterseccion(MatrizInterseccionSalida matrizIntSalida, int coordX, int coordY, int dir) {

        //Calculamos la posicion de la interseccion en la matriz segun las coordenadas */
        int posX = Math.round(coordX / 100);
        int posY = Math.round(coordY / 100);

        return matrizIntSalida.getMatrizInterseccionSalida()[posX][posY].getEstadoVerdeEjeX();
    }

    public ArrayList<ObjCar> genDataVehiculos() {
        ArrayList<ObjCar> vehiculos = new ArrayList<ObjCar>();

        for (int i = 0; i < NUM_VEH; i++) {
            ObjCar auxVeh = new ObjCar();
            int posX;
            int posY;
            int senRandom = (int) (Math.random() * 2);
            int posRandom = (int) (Math.random() * 2);

            if (posRandom == 0) {//Vehiculo en direccion X
                posX = (int) (Math.random() * 1000);
                posY = ((int) (Math.random() * 10)) * 100;
                if (senRandom == 0) {
                    auxVeh.setSentido('E');
                } else {
                    auxVeh.setSentido('O');
                }
            } else {//Vehiculo en direccion Y
                posX = ((int) (Math.random() * 10)) * 100;
                posY = (int) (Math.random() * 1000);
                if (senRandom == 0) {
                    auxVeh.setSentido('N');
                } else {
                    auxVeh.setSentido('S');
                }
            }
            auxVeh.setPosX(posX);
            auxVeh.setPosY(posY);

            vehiculos.add(auxVeh);
        }
        return vehiculos;
    }

    public void ImpNumVehInter(MatrizInterseccionEntrada matrizIntEntrada) {
        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntEntrada.getMatrizInterseccionEntrada()[i][j] != null) {

                    InterseccionEntrada auxInt = matrizIntEntrada.getMatrizInterseccionEntrada()[i][j];
                    System.out.println("Interseccion en i: " + i + " j: " + j);
                    System.out.println("Num Vehiculos N: " + auxInt.getnAutosNorte());
                    System.out.println("Num Vehiculos S: " + auxInt.getnAutosSur());
                    System.out.println("Num Vehiculos E: " + auxInt.getnAutosEste());
                    System.out.println("Num Vehiculos O: " + auxInt.getnAutosOeste());
                }
            }
        }
    }

    public MatrizInterseccionOptima genZonaSinGeneticoOld(MatrizInterseccionEntrada matrizIntEntrada) {
        MatrizInterseccionOptima auxMatrizIntOptima = new MatrizInterseccionOptima(matrizIntEntrada.getxIni(), matrizIntEntrada.getyIni());
        InterseccionOptima[][] auxIntOptima = new InterseccionOptima[LARGO_ZONA][LARGO_ZONA];

        for (int i = 0; i < matrizIntEntrada.getMatrizInterseccionEntrada().length; i++) {
            for (int j = 0; j < matrizIntEntrada.getMatrizInterseccionEntrada()[i].length; j++) {
                if (matrizIntEntrada.getMatrizInterseccionEntrada()[i][j] != null) {
                    InterseccionOptima interOptima = new InterseccionOptima(3);
                    auxIntOptima[i][j] = interOptima;
                }
            }
        }
        auxMatrizIntOptima.setMatrizInterseccionOptima(auxIntOptima);
        return auxMatrizIntOptima;
    }

    public void actZonaSinGenetico(int iniX, int iniY) {
        MatrizInterseccionOptima matrizIntOptima = getMatrizTotalOptima()[iniX][iniY];

        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntOptima.getMatrizInterseccionOptima()[i][j] != null) {
                    matrizIntOptima.getMatrizInterseccionOptima()[i][j].setConfiguracion(3);
                }
            }
        }
    }

    public void actMatTotalOptima() {

        //Actualizo MatrizZonaPatron
        genMatZonaPatron();
        
        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {

                //Zona sin genetico, aqui se debe verificar el patron
                if (getMatrizZonaPatron()[i][j] == true) {//consultarPatron(i*1000, j*1000
                    System.out.println("Genetico aplicado en: " + i + " " + j);
                    MatrizInterseccionEntrada matIntEntrada = copMatIntEntrada(i, j);
                    Genetico genetico = new Genetico(matIntEntrada);
                    //Genetico genetico = new Genetico();
                    genetico.GenSolOptima(i, j);
                } else {
                    //System.out.println("Zona sin genetico: " + i + " " + j);
                    actZonaSinGenetico(i, j);
                }
            }
        }
        hiloGenEjecucion = false;
        System.out.println("Aplicación de Algoritmo Genético finalizado");
    }

    public MatrizInterseccionEntrada copMatIntEntrada(int iniX, int iniY) {
        MatrizInterseccionEntrada matIntEntrada = new MatrizInterseccionEntrada(iniX, iniY);
        InterseccionEntrada[][] auxMatIntEntradaCop = new InterseccionEntrada[LARGO_ZONA][LARGO_ZONA];
        InterseccionEntrada[][] auxMatIntEntradaOri = this.matrizTotalEntrada[iniX][iniY].getMatrizInterseccionEntrada();
        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (this.matrizTotalEntrada[iniX][iniY].getMatrizInterseccionEntrada()[i][j] != null) {
                    InterseccionEntrada auxIntEntrada = new InterseccionEntrada();
                    auxIntEntrada.setnAutosEste(auxMatIntEntradaOri[i][j].getnAutosEste());
                    auxIntEntrada.setnAutosOeste(auxMatIntEntradaOri[i][j].getnAutosOeste());
                    auxIntEntrada.setnAutosNorte(auxMatIntEntradaOri[i][j].getnAutosNorte());
                    auxIntEntrada.setnAutosSur(auxMatIntEntradaOri[i][j].getnAutosSur());
                    auxMatIntEntradaCop[i][j] = auxIntEntrada;
                }

            }
        }
        matIntEntrada.setMatrizInterseccionEntrada(auxMatIntEntradaCop);
        return matIntEntrada;
    }

    public void actMatTotalEntrada() {

        int posMatX;
        int posMatY;
        int auxNumVeh;

        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {

                MatrizInterseccionEntrada matrizIntEntrada = getMatrizTotalEntrada()[i][j];
                if (matrizIntEntrada == null) {
                    System.out.println("matrizIntEntrada NULL");
                }

                //Obtener por RMI    
                ArrayList<ObjCar> vehiculos = genDataVehiculos();
                for (int k = 0; k < vehiculos.size(); k++) {
                    //System.out.println("Sentido: " + vehiculos.get(i).sentido);
                    if (vehiculos.get(k).sentido == 'N') {
                        posMatX = (int) (vehiculos.get(k).getPosX() / 100);
                        posMatY = (int) (vehiculos.get(k).getPosY() / 100) + 1;
//                        System.out.println("posMatX: " + posMatX);
//                        System.out.println("posMatY: " + posMatY);
                        if (posMatX != 10 && posMatY != 10) {
                            if (matrizIntEntrada.getMatrizInterseccionEntrada() != null) {
                                if (matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY] != null) {
                                    auxNumVeh = matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].getnAutosSur();
                                    auxNumVeh++;
                                    matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].setnAutosSur(auxNumVeh);
                                }
                            } else {
                                System.out.println("matrizIntEntrada.getMatrizInterseccionEntrada() NULL");
                            }

                        }
                    } else if (vehiculos.get(k).sentido == 'S') {
                        posMatX = (int) (vehiculos.get(k).getPosX() / 100);
                        posMatY = (int) (vehiculos.get(k).getPosY() / 100);
//                        System.out.println("posMatX: " + posMatX);
//                        System.out.println("posMatY: " + posMatY);
                        if (posMatX != 10 && posMatY != 10) {
                            if (matrizIntEntrada.getMatrizInterseccionEntrada() != null) {
                                if (matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY] != null) {
                                    auxNumVeh = matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].getnAutosNorte();
                                    auxNumVeh++;
                                    matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].setnAutosNorte(auxNumVeh);
                                }
                            } else {
                                System.out.println("matrizIntEntrada.getMatrizInterseccionEntrada() NULL");
                            }
                        }

                    } else if (vehiculos.get(k).sentido == 'E') {
                        posMatX = (int) (vehiculos.get(k).getPosX() / 100);
                        posMatY = (int) (vehiculos.get(k).getPosY() / 100);
//                        System.out.println("posMatX: " + posMatX);
//                        System.out.println("posMatY: " + posMatY);
                        if (posMatX != 10 && posMatY != 10) {
                            if (matrizIntEntrada.getMatrizInterseccionEntrada() != null) {

                                if (matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY] != null) {
                                    auxNumVeh = matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].getnAutosOeste();
                                    auxNumVeh++;
                                    matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].setnAutosOeste(auxNumVeh);
                                }
                            } else {
                                System.out.println("matrizIntEntrada.getMatrizInterseccionEntrada() NULL");
                            }
                        }
                    } else if (vehiculos.get(k).sentido == 'O') {
                        posMatX = (int) (vehiculos.get(k).getPosX() / 100) + 1;
                        posMatY = (int) (vehiculos.get(k).getPosY() / 100);
//                        System.out.println("posMatX: " + posMatX);
//                        System.out.println("posMatY: " + posMatY);
                        if (posMatX != 10 && posMatY != 10) {
                            if (matrizIntEntrada.getMatrizInterseccionEntrada() != null) {
                                if (matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY] != null) {
                                    auxNumVeh = matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].getnAutosEste();
                                    auxNumVeh++;
                                    matrizIntEntrada.getMatrizInterseccionEntrada()[posMatX][posMatY].setnAutosEste(auxNumVeh);
                                }
                            } else {
                                System.out.println("matrizIntEntrada.getMatrizInterseccionEntrada() NULL");
                            }
                        }
                    }
                }
            }
        }
    }

    public void iniSimIntersecciones() {

        System.out.println("Iniciando Simulación de Semaforos...");
        System.out.println("Inicializando MatrizTotalOptima...");
        iniMatrizTotalOptima();
        System.out.println("Inicializando MatrizTotalEntrada...");
        iniMatrizTotalEntrada();
        System.out.println("Inicializando MatrizTotalSalida...");
        iniMatrizTotalSalida();
        System.out.println("Actualizando MatTotalEntrada...");
        actMatTotalEntrada();
        //pruebaMatrizSalida();

        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {
                //Cada hilo actualiza 64 zonas
                if ((i % 4 == 0) && (j % 4 == 0)) {
                    //System.out.println("Hilo creado Inicio: " + i + " " + j);
                    HiloSimZona hiloSimZona = new HiloSimZona(i, j);
                    hiloSimZona.start();
                }
            }
        }
        HiloGeneral hiloGeneral = new HiloGeneral();
        hiloGeneral.start();

        //iniHiloGenetico();
        System.out.println("Simulación en ejecución");
    }

    public boolean consultarPatron(int coordX, int coordY) {
        boolean auxBool = false;
        try {
            BaseDatos bd = new BaseDatos();
            //bd.GetZonasAAplicarAlgoritmo(i, j)

            ArrayList zonasConAlgoritmo = bd.GetZonasAAplicarAlgoritmo(obtDiaSemana(), obtHoraDia());
            //System.out.println("Size:" +zonasConAlgoritmo.size());

            for (int i = 0; i < zonasConAlgoritmo.size(); i++) {
                Punto auxPunto = bd.CoordenadasDeZona((int) zonasConAlgoritmo.get(i));
                if (coordX == auxPunto.getX() && coordY == auxPunto.getY()) {
                    auxBool = true;
                }
                //System.out.println("Zona: "+zonasConAlgoritmo.get(i)+"-"+auxPunto.getX()+"-"+auxPunto.getY());
            }
        } catch (Exception e) {
            System.out.println("No hay conexion a BD consultarPatron");
        }
        return auxBool;
    }

    public void genMatZonaPatron() {
        try {
            BaseDatos bd = new BaseDatos();
            //bd.GetZonasAAplicarAlgoritmo(i, j)

            ArrayList zonasConAlgoritmo = bd.GetZonasAAplicarAlgoritmo(obtDiaSemana(), obtHoraDia());
            //System.out.println("Size:" +zonasConAlgoritmo.size());

            for (int i = 0; i < zonasConAlgoritmo.size(); i++) {
                Punto auxPunto = CoordenadasDeZona((int) zonasConAlgoritmo.get(i));
                //System.out.println("PuntoX: "+auxPunto.getX());
                //System.out.println("PuntoY: "+auxPunto.getY());
                //Validacion de limites
                if (((int) (auxPunto.getX() / 1000)) < nZonasLargo && ((int) (auxPunto.getY() / 1000)) < nZonasAlto) {
                    matrizZonaPatron[(int) auxPunto.getX() / 1000][(int) auxPunto.getY() / 1000] = true;
                    
                    //System.out.println("Zona: " + zonasConAlgoritmo.get(i) + "-" + auxPunto.getX() + "-" + auxPunto.getY());
                } else {
                    System.out.println("Fuera de los limites genMatZonaPatron: " + ((int) (auxPunto.getX() / 1000)) + "-" + ((int) (auxPunto.getY() / 1000)));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Punto CoordenadasDeZona(int zona) {
        //JAVIER: las zonas se nominan horizontalmente, empezando por la zona 0 (Cero)
        Punto rpta = new Punto(0, 0);
        int fila = zona / 240;
        double columna = (double) zona % (double) 240;
        int x = (int) columna * 1000;
        int y = (fila) * 1000;
        rpta.setX(x);
        rpta.setY(y);
        return rpta;
    }

    public void iniMatrizTotalOptima() {
        try {
            BaseDatos bd = new BaseDatos();
            ArrayList<Punto> auxInter = bd.InterseccionesDeMapa();
            System.out.println("Número de intersecciones señalizadas: " + auxInter.size());

            for (int i = 0; i < nZonasLargo; i++) {
                for (int j = 0; j < nZonasAlto; j++) {
                    MatrizInterseccionOptima auxMatIntOptima = new MatrizInterseccionOptima(i * 1000, j * 1000);
                    InterseccionOptima[][] auxInterOptima = new InterseccionOptima[LARGO_ZONA][LARGO_ZONA];
                    auxMatIntOptima.setMatrizInterseccionOptima(auxInterOptima);
                    this.matrizTotalOptima[i][j] = auxMatIntOptima;
                }
            }

            for (int k = 0; k < auxInter.size(); k++) {
                InterseccionOptima auxIntOptima = new InterseccionOptima(3);//3 = 60 Verde 60 Rojo
                //CAMBIAR getX por getY, CORREGIR BD x<240000 y<160000
                int posXZona = (int) auxInter.get(k).getX() / 1000;
                int posYZona = (int) auxInter.get(k).getY() / 1000;
                int posXSem = (int) (auxInter.get(k).getX() % 1000) / 100;
                int posYSem = (int) (auxInter.get(k).getY() % 1000) / 100;
                /*if (k % 64 == 0) {
                 System.out.println("K: " + k);
                 System.out.println("posXZona: "+posXZona);
                 System.out.println("posXSem: "+posXSem);
                    
                 }*/
                matrizTotalOptima[posXZona][posYZona].getMatrizInterseccionOptima()[posXSem][posYSem] = auxIntOptima;
            }
        } catch (Exception e) {
            System.out.println("Fallo iniMatrizTotalOptima");
            e.printStackTrace();
        }
    }

    public void iniMatrizTotalSalida() {
        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {
                if (this.getMatrizTotalOptima()[i][j] != null) {
                    MatrizInterseccionSalida auxMatTotalIntSalida = new MatrizInterseccionSalida(i * 1000, j * 1000);
                    InterseccionSalida[][] auxMatInterSalida = new InterseccionSalida[LARGO_ZONA][LARGO_ZONA];
                    for (int k = 0; k < LARGO_ZONA; k++) {
                        for (int l = 0; l < LARGO_ZONA; l++) {
                            if (this.getMatrizTotalOptima()[i][j].getMatrizInterseccionOptima()[k][l] != null) {
                                //Estado y tiempoRestante se actualizan en los hilos
                                int auxConfig = this.getMatrizTotalOptima()[i][j].getMatrizInterseccionOptima()[k][l].getConfiguracion();
                                int auxDurTiempoVerHor = tablaConfig.ObtenerConfiguracion(auxConfig).getTiempoVerdeHor();
                                InterseccionSalida auxIntSalida = new InterseccionSalida(auxDurTiempoVerHor, 0);// Siempre inicia en verde horizontal
                                auxMatInterSalida[k][l] = auxIntSalida;
                            }
                        }
                    }
                    auxMatTotalIntSalida.setMatrizInterseccionSalida(auxMatInterSalida);
                    this.matrizTotalSalida[i][j] = auxMatTotalIntSalida;
                }
            }
        }
    }

    public void iniMatrizTotalEntrada() {
        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {
                if (getMatrizTotalOptima()[i][j] != null) {
                    MatrizInterseccionEntrada auxMatTotalIntEntrada = new MatrizInterseccionEntrada(i * 1000, j * 1000);
                    InterseccionEntrada[][] auxMatInterEntrada = new InterseccionEntrada[LARGO_ZONA][LARGO_ZONA];
                    for (int k = 0; k < LARGO_ZONA; k++) {
                        for (int l = 0; l < LARGO_ZONA; l++) {
                            if (this.getMatrizTotalOptima()[i][j].getMatrizInterseccionOptima()[k][l] != null) {
                                //Obtener los vehiculos en Matriz de Entrada
                                InterseccionEntrada auxIntEntrada = new InterseccionEntrada(0, 0, 0, 0);
                                auxMatInterEntrada[k][l] = auxIntEntrada;
                            }
                        }
                    }
                    auxMatTotalIntEntrada.setMatrizInterseccionEntrada(auxMatInterEntrada);
                    this.matrizTotalEntrada[i][j] = auxMatTotalIntEntrada;
                } else {
                    System.out.println("this.getMatrizTotalOptima()[i][j] NULL");
                }
            }
        }
    }

    public void pruebaMatrizSalida() {
        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {
                System.out.println("prueba: " + getMatrizTotalSalida()[i][j].getxIni());
                if (this.getMatrizTotalSalida()[i][j] != null) {

                    for (int k = 0; k < LARGO_ZONA; k++) {
                        for (int l = 0; l < LARGO_ZONA; l++) {
                            if (this.getMatrizTotalSalida()[i][j].getMatrizInterseccionSalida()[k][l] != null) {
                                System.out.println("TEST: " + this.getMatrizTotalSalida()[i][j].getMatrizInterseccionSalida()[k][l].getTiempoRestante());
                            } else {
                                System.out.println("TEST 3");
                            }
                        }
                    }
                }
            }
        }
    }

    public void ActMatTotalIntSalida(int iniX, int iniY) {
        MatrizInterseccionSalida matrizIntSalida = getMatrizTotalSalida()[iniX][iniY];
        MatrizInterseccionOptima matrizIntOptima = getMatrizTotalOptima()[iniX][iniY];

        for (int i = 0; i < LARGO_ZONA; i++) {
            for (int j = 0; j < LARGO_ZONA; j++) {
                if (matrizIntSalida.getMatrizInterseccionSalida()[i][j] != null) {
                    int auxTiempoRestante = matrizIntSalida.getMatrizInterseccionSalida()[i][j].getTiempoRestante();
                    if (auxTiempoRestante == 1) {//Si le resta 1 seg, le cambia de estadoVerdeEjeX

                        //matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(3);
                        int auxEstado = matrizIntSalida.getMatrizInterseccionSalida()[i][j].getEstadoVerdeEjeX();
                        if (auxEstado == 0) {//de 0 a 1, y de 1 a 0

                            int auxConfig = matrizIntOptima.getMatrizInterseccionOptima()[i][j].getConfiguracion();
                            int auxNuevoTiempo = tablaConfig.ObtenerConfiguracion(auxConfig).getTiempoVerdeVer();//Cambio a tiempo Vertical
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setEstadoVerdeEjeX(1);
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(auxNuevoTiempo);
                        } else {
                            int auxConfig = matrizIntOptima.getMatrizInterseccionOptima()[i][j].getConfiguracion();
                            int auxNuevoTiempo = tablaConfig.ObtenerConfiguracion(auxConfig).getTiempoVerdeHor();//Cambio a tiempo Vertical
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setEstadoVerdeEjeX(0);
                            matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(auxNuevoTiempo);
                        }
                    } else {
                        //Sino hay cambio de estado
                        matrizIntSalida.getMatrizInterseccionSalida()[i][j].setTiempoRestante(--auxTiempoRestante);
                    }
                }
            }
        }
    }
    /*Edgard: Devuelve una zona según las coordenadas ingresadas */

    public MatrizInterseccionSalida[][] obtTotalInter() {
        MatrizInterseccionSalida[][] todasMatrizIntSalida = new MatrizInterseccionSalida[nZonasLargo][nZonasAlto];

        for (int i = 0; i < nZonasLargo; i++) {
            for (int j = 0; j < nZonasAlto; j++) {
                todasMatrizIntSalida[i][j] = matrizHiloZona[i][j].getMatrizIntSalida();
            }
        }
        return todasMatrizIntSalida;
    }

    public MatrizInterseccionSalida obtInterPorCoord(int coordX, int coordY) {
        MatrizInterseccionSalida auxMatInterSalida;
        int posX = (int) coordX / 1000;
        int posY = (int) coordY / 1000;
        auxMatInterSalida = getMatrizTotalSalida()[posX][posY];
        return auxMatInterSalida;
    }

    public static int obtDiaSemana() {
        Date d = new Date();
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(d);

        int dia = cal.get(Calendar.DAY_OF_WEEK);
        if (dia == 1) {
            dia = 7;
        } else {
            dia--;
        }
        return dia;
    }

    public static int obtHoraDia() {
        Calendar calendario = new GregorianCalendar();
        int hora = calendario.get(Calendar.HOUR_OF_DAY);
        return hora;
    }

    public int getnZonasLargo() {
        return nZonasLargo;
    }

    public void setnZonasLargo(int nZonasLargo) {
        this.nZonasLargo = nZonasLargo;
    }

    public int getnZonasAlto() {
        return nZonasAlto;
    }

    public void setnZonasAlto(int nZonasAlto) {
        this.nZonasAlto = nZonasAlto;
    }

    public MatrizInterseccionEntrada[][] getMatrizTotalEntrada() {
        return matrizTotalEntrada;
    }

    public MatrizInterseccionOptima[][] getMatrizTotalOptima() {
        return matrizTotalOptima;
    }

    public MatrizInterseccionSalida[][] getMatrizTotalSalida() {
        return matrizTotalSalida;
    }

    public boolean iniHiloGenetico() {
        if (!hiloGenEjecucion) {
            System.out.println("Iniciando aplicación de algoritmo genético...");
            hiloGenEjecucion = true;
            HiloGenetico hiloGenetico = new HiloGenetico();
            hiloGenetico.run();
            return true;
        } else {
            System.out.println("Algoritmo genetico aún en curso...");
            return false;
        }
    }

    public boolean agregarSemaforo(int xCoord, int yCoord) {
        boolean auxReturn = false;
        try {
            BaseDatos bd = new BaseDatos();
            if (bd.AgregarInterseccion(xCoord, yCoord)) {
                //Agregamos a MatrizIntOptima
                InterseccionOptima interOptima = new InterseccionOptima(3);

                int xPosMapa = xCoord / 1000;
                int yPosMapa = yCoord / 1000;
                int xPosZona = (xCoord % 1000) / 100;
                int yPosZona = (yCoord % 1000) / 100;
                matrizTotalOptima[xPosMapa][yPosMapa].getMatrizInterseccionOptima()[xPosZona][yPosZona] = interOptima;

                InterseccionEntrada interEntrada = new InterseccionEntrada(0, 0, 0, 0);
                matrizTotalEntrada[xPosMapa][yPosMapa].getMatrizInterseccionEntrada()[xPosZona][yPosZona] = interEntrada;

                InterseccionSalida interSalida = new InterseccionSalida(periodoSemaforo, 0);
                matrizTotalSalida[xPosMapa][yPosMapa].getMatrizInterseccionSalida()[xPosZona][yPosZona] = interSalida;

                auxReturn = true;
            }
        } catch (Exception e) {
        }
        return auxReturn;
    }

    public boolean eliminarSemaforo(int xCoord, int yCoord) {
        boolean auxReturn = false;
        try {
            BaseDatos bd = new BaseDatos();
            if (bd.EliminarInterseccion(xCoord, yCoord)) {

                int xPosMapa = xCoord / 1000;
                int yPosMapa = yCoord / 1000;
                int xPosZona = (xCoord % 1000) / 100;
                int yPosZona = (yCoord % 1000) / 100;

                matrizTotalOptima[xPosMapa][yPosMapa].getMatrizInterseccionOptima()[xPosZona][yPosZona] = null;
                matrizTotalEntrada[xPosMapa][yPosMapa].getMatrizInterseccionEntrada()[xPosZona][yPosZona] = null;
                matrizTotalSalida[xPosMapa][yPosMapa].getMatrizInterseccionSalida()[xPosZona][yPosZona] = null;

                auxReturn = true;
            }
        } catch (Exception e) {
        }
        return auxReturn;
    }

    public int getPeriodoSemaforo() {
        return periodoSemaforo;
    }

    public void setPeriodoSemaforo(int periodoSemaforo) {
        this.periodoSemaforo = periodoSemaforo;
    }

    public int getPeriodoGenetico() {
        return periodoGenetico;
    }

    public void setPeriodoGenetico(int periodoGenetico) {
        this.periodoGenetico = periodoGenetico;
    }

    public boolean isHiloGenEjecucion() {
        return hiloGenEjecucion;
    }

    public void setHiloGenEjecucion(boolean hiloGenEjecucion) {
        this.hiloGenEjecucion = hiloGenEjecucion;
    }

    public boolean[][] getMatrizZonaPatron() {
        return matrizZonaPatron;
    }
}
